package com.liangwj.springbootquickstart.jdk8.functionalInterface;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * <b>Description:</b> 函数式编程接口的使用 <br/>
 * @author Lingwenjun
 * @date 2019/1/11-17:25
 * @version 1.0
 */
public class FunctionalInterfaceUsage {
    /**
     * Stream
     */
    /**
     * Stream的创建
     */
    public static void createStream() {
        /**
         * 1. 创建空的Stream对象
         */
        Stream stream = Stream.empty();
        stream.close();

        //2. 通过集合类中的stream或parallelStream方法创建
        List<String> list = Arrays.asList("a", "b", "c", "d");
        Stream stream1 = list.stream();//串行
        stream1.close();
        Stream pStream1 = list.parallelStream();//并行

        //3. 通过Stream中的of方法创建
        Stream ofStream = Stream.of(list);
        Stream ofStream1 = Stream.of("test");
        Stream ofStream2 = Stream.of(1, 2, 3);

        //4. 通过Stream中的iterate方法创建
        //Iterate 方法有两个不同参数的方法
            //4.1 无限有序值,一般搭配limit使用
            //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
            Stream.iterate(0, i -> i++ ).limit(5);//.forEach(System.out::println);

            //4.2 (WARN:jdk-1.9 才有!!!!!)有限值,中止条件是由hasNext来断定的
            //public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next);
            //Stream.iterate(0, i -> i < 10, i -> i++);

        //5. 通过Stream中的generate方法创建
        // 与iterate中创建无限元素的Stream类似，不过它的每个元素与前一元素无关，且生成的是一个无序的队列。
        // 也就是说每一个元素都可以随机生成。因此一般用来创建常量的Stream以及随机的Stream等。
            // 随机生成10个Double 元素的Stream并将其打印
            Stream.generate(new Supplier<Double>() {
                @Override
                public Double get() {
                    return Math.random();
                }
            }).limit(10)
            //打印输出
            .forEach(System.out::println);
            //简写:
            Stream.generate(Math::random).limit(10).forEach(System.out::println);

        //6 通过Stream中的concat方法连接2个Stream对象生成新的Stream对象
        Stream.concat(Stream.of(""), Stream.generate(Math::random));
    }

    /**
     * Stream对象的使用
     * Stream对象提供多个非常有用的方法,分成两类:
     *  1. 中间操作: 将原始的Stream转换成另一个Stream; 如filter返回的是过滤后的Stream
     *  2. 终端操作: 产生的是一个结果或者其他的复合操作, 如count或者forEach操作
     */
    public static void usageStream() {
        /**
         * filter
         * 用于对Stream中的元素进行过滤，返回一个过滤后的Stream
         */
        Stream<String> sFilter = Stream.of("test1", "t1", "test2", "t2");
        sFilter.filter(n -> n.contains("t")).forEach(System.out::println); //查找包含"t"的元素并打印

        /**
         * map
         * 元素一对一转换: 它接收一个Function参数, 用其对Stream中的所有元素进行处理, 返回的Stream对象中的元素为Function对原元素处理后的结果
         */
        Stream<String> sMap = Stream.of("test1", "t1", "test2", "t2");
        sMap.map(s -> s.concat("a")).forEach(System.out::println);

        /**
         * flatMap
         * 元素一对多转换：对原Stream中的所有元素使用传入的Function进行处理，每个元素经过处理后生成一个多个元素的Stream对象，
         * 然后将返回的所有Stream对象中的所有元素组合成一个统一的Stream并返回；
         */
        Stream<String> sFlatMap = Stream.of("test1", "t1", "test2", "t2");
        sFlatMap.flatMap(n -> Stream.of(n.split(""))).forEach(System.out::println);
        sFlatMap.flatMapToInt(n -> IntStream.of(n.indexOf(""))).forEach(System.out::println);

        /**
         * takeWhile
         * 如果Stream是有序的(Ordered), 那么返回最长命中序列 (符合传入的Predicate的最长命中序列)组成的Stream
         * 如果Stream是无序的, 那么返回的是所有符合传入的Predicate的元素序列组成的Stream
         * 与filter 类似, 不同的地方就在当Stream是有序时, 返回的只是最长命中序列
         */
        Stream<String> sTakeWhile = Stream.of("test1", "t1", "test2", "t2", "asdasd", "taaaa");
        //输出 [test1, t1, test2, t2]; taaaa不会输出
        //sTakeWhile.takeWhile( n -> n.contains("t")).forEach(System.out::println);

        /**
         * dropWhile
         * 与takeWhile相反
         */
        Stream<String> sDropWhile = Stream.of("test1", "t1", "test2", "t2", "asdasd", "taaaa");
        //输出 [asdasd, taaaa]; 其他的不会输出
        //sDropWhile.dropWhile( n -> n.contains("t")).forEach(System.out::println);
    }

    /**
     * Optional
     * 用于java中对空值的判断处理, 以防止出现各种空指针异常
     * Optional实际上是对一个变量进行封装, 它包含有一个属性value, 实际上就是这个变量的值
     */
    /**
     * Optional对象的创建
     * 它的构造函数都是private类型的, 因此初始化一个Optional的对象无法通过构造函数, 使用其提供的一些静态方法构造对象
     */
    public static void createOptional() throws Exception {
        //empty :用于创建一个空的Optional对象；其value属性为Null。
        Optional optional = Optional.empty();

        //of :传入的值必须是非空值，否则如果传入的值为空值，则会抛出空指针异常。
        optional = Optional.of("test");
        //System.out.println("optional = " + optional.get().toString());

        //ofNullable :传入的值可以是空值，如果传入的值是空值，则与empty返回的结果是一样的
        Optional.ofNullable("test").ifPresent(System.out::println);

        //使用场景
        Optional<String> s = Optional.ofNullable(test());
        s.ifPresent(System.out::println);

        //变量为空时提供默认值
        Optional<String> o = Optional.ofNullable(null);
        System.out.println(o.orElse("test"));

        //变量为空时抛出异常，否则使用
        Optional<String> op = Optional.ofNullable(null);
        op.orElseThrow(() -> new Exception("test"));
    }

    private static String test() {
        return null;
    }

    public static void main(String[] args) {
        FunctionalInterfaceUsage.createStream();
        FunctionalInterfaceUsage.usageStream();
        try {
            FunctionalInterfaceUsage.createOptional();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
